﻿using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using MadMediaManager.WPFSupport.Interfaces.Services;
using MadMediaManager.WPFSupport.Interfaces.Views;

namespace MadMediaManager.WPFSupport.Services
{
    public enum TransitionType
    {
        DoublePaneRightToLeft,
        DoublePaneLeftToRight,
        SinglePaneRightToLeft,
        SinglePaneLeftToRight,
        Fade,
        None
    }

    public class Transitioner : ITransitioner
    {
        internal enum AnimationDirection { LeftToRight, RightToLeft }
        public const double ANIMATION_DURATION = 400;

        private static void DoublePaneTransition(FrameworkElement currentView, FrameworkElement newView, AnimationDirection animationDirection)
        {
            if (currentView == null)
            {
                SinglePaneTransition(currentView, newView, animationDirection);
                return;
            }

            double splineValue = GetAnimationDouble(newView.ActualWidth + 10, animationDirection);

            const string newViewTransformName = "newtransform";
            const string currentViewTransformName = "currenttransform";

            var storyboard = new Storyboard();
            NameScope.SetNameScope(currentView, new NameScope());

            var currentViewTransform = currentView.RenderTransform as TranslateTransform;
            if (currentViewTransform == null)
            {
                currentViewTransform = new TranslateTransform();
                currentView.RenderTransformOrigin = new Point(0.5, 0.5);
                currentView.RenderTransform = currentViewTransform;
            }

            currentView.RegisterName(currentViewTransformName, currentViewTransform);

            var newViewDoubleAnimation = new DoubleAnimationUsingKeyFrames { BeginTime = TimeSpan.FromSeconds(0) };
            Storyboard.SetTargetName(newViewDoubleAnimation, newViewTransformName);
            Storyboard.SetTargetProperty(newViewDoubleAnimation, new PropertyPath("X"));
            newViewDoubleAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(splineValue,
                                                                          KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0))));
            newViewDoubleAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(0,
                                                                          KeyTime.FromTimeSpan(
                                                                              TimeSpan.FromMilliseconds(
                                                                                  ANIMATION_DURATION))));

            storyboard.Children.Add(newViewDoubleAnimation);

            var newViewTransform = newView.RenderTransform as TranslateTransform;
            if (newViewTransform == null)
            {
                newViewTransform = new TranslateTransform();
                newView.RenderTransformOrigin = new Point(0.5, 0.5);
                newView.RenderTransform = newViewTransform;
            }

            currentView.RegisterName(newViewTransformName, newViewTransform);

            var currentViewDoubleAnimation = new DoubleAnimationUsingKeyFrames { BeginTime = TimeSpan.FromSeconds(0), FillBehavior = FillBehavior.Stop };
            Storyboard.SetTargetName(currentViewDoubleAnimation, currentViewTransformName);
            Storyboard.SetTargetProperty(currentViewDoubleAnimation, new PropertyPath("X"));
            currentViewDoubleAnimation.KeyFrames.Add(new SplineDoubleKeyFrame(splineValue * -1,
                                                                              KeyTime.FromTimeSpan(
                                                                                  TimeSpan.FromMilliseconds(
                                                                                      ANIMATION_DURATION))));

            storyboard.Children.Add(currentViewDoubleAnimation);

            storyboard.Begin(currentView);
        }

        private static void Fade(IAnimatable currentView, FrameworkElement newView)
        {
            if (currentView == null)
            {
                newView.BeginAnimation(UIElement.OpacityProperty,
                                       new DoubleAnimation(0, 1, new Duration(TimeSpan.FromMilliseconds(ANIMATION_DURATION))));
            }
            else
            {
                currentView.BeginAnimation(UIElement.OpacityProperty,
                                           new DoubleAnimation(1, 0,
                                                               new Duration(TimeSpan.FromMilliseconds(ANIMATION_DURATION)),
                                                               FillBehavior.Stop));
                newView.BeginAnimation(UIElement.OpacityProperty,
                                       new DoubleAnimation(0, 1, new Duration(TimeSpan.FromMilliseconds(ANIMATION_DURATION))));
            }
        }

        private static void None(UIElement currentView)
        {
            if (currentView != null)
            {
                currentView.Visibility = Visibility.Collapsed;
            }
        }

        private static void SinglePaneTransition(UIElement currentView, FrameworkElement newView, AnimationDirection animationDirection)
        {
            var newViewTransform = newView.RenderTransform as TranslateTransform;

            if (newViewTransform == null)
            {
                newViewTransform = new TranslateTransform();
                newView.RenderTransformOrigin = new Point(0.5, 0.5);
                newView.RenderTransform = newViewTransform;
            }

            double fromValue = GetAnimationDouble(newView.ActualWidth + 20, animationDirection);

            newViewTransform.BeginAnimation(TranslateTransform.XProperty,
                                            new DoubleAnimation(fromValue, 0, new Duration(TimeSpan.FromMilliseconds(ANIMATION_DURATION))) { DecelerationRatio = 1 });

            if (currentView != null)
            {
                currentView.Visibility = Visibility.Collapsed;
            }
        }

        private static double GetAnimationDouble(double value, AnimationDirection animationDirection)
        {
            return animationDirection == AnimationDirection.LeftToRight ? value*-1 : value;
        }

        public void Transition(INavigableView currentView, INavigableView newView, TransitionType transitionType)
        {
            switch (transitionType)
            {
                case TransitionType.DoublePaneLeftToRight:
                    DoublePaneTransition(currentView as FrameworkElement, newView as FrameworkElement, AnimationDirection.LeftToRight);
                    break;
                case TransitionType.DoublePaneRightToLeft:
                    DoublePaneTransition(currentView as FrameworkElement, newView as FrameworkElement, AnimationDirection.RightToLeft);
                    break;
                case TransitionType.Fade:
                    Fade(currentView as FrameworkElement, newView as FrameworkElement);
                    break;
                case TransitionType.SinglePaneLeftToRight:
                    SinglePaneTransition(currentView as FrameworkElement, newView as FrameworkElement, AnimationDirection.LeftToRight);
                    break;
                case TransitionType.SinglePaneRightToLeft:
                    SinglePaneTransition(currentView as FrameworkElement, newView as FrameworkElement, AnimationDirection.RightToLeft);
                    break;
                case TransitionType.None:
                    None(currentView as FrameworkElement);
                    break;
                default:
                    throw new NotSupportedException(transitionType + " is not supported for transitioning");
            }
        }
    }
}
